// ======================================================================
// Copyright (C) 2013 Hell-Prototypes. / www.hellprototypes.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// ======================================================================
#include <QApplication>
#include "hell_arm.h"
#include "ui_hell_arm.h"
#include "cv.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);    Hell_Arm w;    w.show();    return a.exec();
}

Hell_Arm::Hell_Arm(QWidget *parent) : QWidget(parent), ui(new Ui::Hell_Arm)
{
    ui->setupUi(this);
    setFocus();

    ui_init();
    m_exe_origin_auto_correct = false;
    m_origin_align_flag = false;
    m_feeder_have_parts = true;
    m_Key_Control_Pressed = false;
    m_config_file = NULL;
    m_origin_zone_area = 0;
    m_origin_zone_size.setWidth(ui->hsb_set_feeder_zone_width->value());
    m_origin_zone_size.setHeight(ui->hsb_set_feeder_zone_height->value());

    m_vision_thread = new vision_thread( this );
    m_vision_thread->set_threshold(ui->sb_parts_threshold->value());
    connect(m_vision_thread, SIGNAL(frame_update(IplImage* )),
                             SLOT(slot_parts_frame_update(IplImage*)));
    connect(m_vision_thread, SIGNAL(debug_image_update(const char *  , IplImage* )),
                             SLOT(slot_debug_image_update(const char * , IplImage* )));
    connect(m_vision_thread, SIGNAL(camera_image_size(int, int)),
                             SLOT(slot_camera_image_size(int, int)));
    connect(m_vision_thread, SIGNAL(parts_position_info(vector<part_t> *)),
                             SLOT(slot_parts_position_info_process(vector<part_t> *)));

    m_pap_thread = new pap_thread(this);
    connect(m_pap_thread, SIGNAL(disp_system_msg(QString)), SLOT(disp_system_msg(QString)));
    connect(m_pap_thread, SIGNAL(xy_axis_pos(int, int)), SLOT(slot_xy_axis_pos(int, int)));
    connect(m_pap_thread, SIGNAL(usb_connected()), SLOT(slot_usb_connected()));
    connect(m_pap_thread, SIGNAL(controller_status_update(int)), m_controler_status, SLOT(slot_controller_status_update(int)));
    m_pap_thread->set_cam_width_length_mm(ui->dsb_cam_width_length->value());

    m_auto_pap_timer.setSingleShot(true);
    connect(&m_auto_pap_timer, SIGNAL(timeout()), SLOT(slot_auto_pap_timer_timeout()));

    coor_list_view_init();

    m_pap_thread->m_usb_dev = NULL; /* the device handle */

    load_config();

    if(!m_pap_thread->usb_connect_ctrl()) {
        ui->pb_connect_device->setText("Connect...");
    } else {
        ui->pb_connect_device->setText("Disconnect");
    }

    this->startTimer(500);
}

Hell_Arm::~Hell_Arm()
{
    m_pap_thread->move_arm_to_origin();
    m_pap_thread->turn_pump_on(false);
    if(m_vision_thread->isRunning()) {
        m_vision_thread->loop_run(false);
        m_vision_thread->wait();
    }
    cvDestroyAllWindows();
    delete m_vision_thread;

    if(m_pap_thread->isRunning()) {
        m_pap_thread->wait();
        delete m_pap_thread;
    }

    delete ui;
}

void Hell_Arm::timerEvent ( QTimerEvent * event )
{
    this->killTimer(event->timerId());
    load_feeder_config();

    m_vision_thread->loop_run(true);
    m_vision_thread->set_capture_index(ui->cb_parts_camera_index->currentIndex());
    m_vision_thread->start();
}

void Hell_Arm::slot_usb_connected()
{
    m_pap_thread->init_arm_position();
}

void Hell_Arm::closeEvent ( QCloseEvent * e )
{
    if(ui->pb_auto_pick_and_place->isChecked()) {
        disp_system_msg("Can't Close, Please Stop Auto PAP First ...");
        e->ignore();
    } else {
        //For close debug image window
        ui->cb_disp_debug_image->setChecked(false);
        store_config();
        e->accept();
    }
}

void Hell_Arm::focusOutEvent ( QFocusEvent * event )
{
    Q_UNUSED(event);

    if(ui->pb_lock_focus_for_move->isChecked()) {
        this->setFocus();
    }
}

void Hell_Arm::keyPressEvent ( QKeyEvent * event )
{
    if(is_move_ctrl_key (event->key())) {
        move_by_ctrl_key ( event->key() );
    } else if(event->key() == Qt::Key_Control) {
        m_Key_Control_Pressed = true;
    }
}
void Hell_Arm::keyReleaseEvent ( QKeyEvent * event )
{
    if(event->key() == Qt::Key_Control) {
        m_Key_Control_Pressed = false;
    }
}

void Hell_Arm::ui_init()
{
    ui->dw_capture_property_setting->setVisible(false);

    m_controler_status = new controler_status(this);
    ui->hl_controler_status->addWidget(m_controler_status);

    ui->lcd_x_axis_offset->setStyleSheet("background-color:black;color:red;");
    ui->lcd_y_axis_offset->setStyleSheet("background-color:black;color:red;");
}

void Hell_Arm::coor_list_view_init()
{
    m_coor_list_view = ui->tv_coordinate_list;
    m_value_list_view = ui->lw_parts_value_list;
    m_coor_list_model = new QStandardItemModel();
    m_coor_list_view->setModel(m_coor_list_model);
    m_coor_list_view->resizeColumnsToContents();
    m_coor_list_view->setGridStyle(Qt::SolidLine);

    QList <int> size;
    size << 48 << 1024;
    ui->slp_coor_list_splitter->setSizes(size);

    connect(m_value_list_view, SIGNAL(itemDoubleClicked ( QListWidgetItem * )), SLOT(slot_value_list_itemDoubleClicked( QListWidgetItem * )));
}

void Hell_Arm::slot_camera_image_size(int w, int h)
{
    ui->hsb_set_feeder_zone_width->setMaximum(w);
    ui->sb_set_part_area->setMaximum(w*h);

    if(m_origin_zone_size.isValid()) {
        m_vision_thread->set_feeder_zone_size(m_origin_zone_size);
    } else {
        m_vision_thread->set_feeder_zone_size(QSize(w/2, h/2));
    }

    m_pap_thread->set_image_size(w, h);
    ui->cb_parts_realtime_view->setChecked(true);
}

void Hell_Arm::on_cb_parts_camera_index_currentIndexChanged(int index)
{
    //FIXME
    /* start capturing frames from camera: index = camera_index + domain_offset (CV_CAP_*) */
    //CVAPI(CvCapture*) cvCreateCameraCapture( int index );
    if(m_vision_thread->isRunning()) {
        m_vision_thread->loop_run(false);
        m_vision_thread->wait();
        if(m_vision_thread->set_capture_index(index)) {
            m_vision_thread->loop_run(true);
            m_vision_thread->start();
        }
    } else {
        if(m_vision_thread->set_capture_index(index)) {
            m_vision_thread->start();
        }
    }
}

void Hell_Arm::on_sb_parts_threshold_valueChanged(int arg)
{
    m_vision_thread->set_threshold(arg);
}

void Hell_Arm::on_cb_parts_realtime_view_clicked(bool checked)
{
    if(checked) {
        if(!m_vision_thread->isRunning()) {
            m_vision_thread->loop_run(true);
            m_vision_thread->start();
       }
    } else {
        if(m_vision_thread->isRunning()) {
            m_vision_thread->loop_run(false);
            m_vision_thread->wait();
        }
    }
}

void Hell_Arm::on_cb_disp_debug_image_clicked(bool checked)
{
    if(checked) {
        m_vision_thread->disp_binary_image(true);
    }
}

void Hell_Arm::on_pb_import_coor_file_clicked()
{
    QString input_path;

    input_path = QFileDialog::getOpenFileName(this, "Open file", "", tr("All (*.*)"));
    if(input_path.isEmpty() == true) {
        return;
    }

    import_coordinate_list_file(input_path);
}

void Hell_Arm::disp_system_msg(QString msg)
{
    ui->pte_system_msg->appendPlainText(msg);
}

void Hell_Arm::on_pb_set_mark_0_clicked()
{
    pcb_mark_param mark_param;

    if(m_pap_thread->m_pcb_mark_list.contains(0)) {
        QMessageBox::StandardButton ret;

        ret = QMessageBox::warning(this, tr("Mark set warning"), "Confirm to Resetting Mark 0?",
                                         QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
        if(ret != QMessageBox::Yes) {
            return;
        }
    }

    if(!get_current_part_coordinate(mark_param.mark_coor)) {
        disp_system_msg("Error: Please select one mark in list");
        return;
    }
    mark_param.arm_pos = m_pap_thread->m_curr_arm_pos;

    m_pap_thread->m_pcb_mark_list[0] = mark_param;
}

void Hell_Arm::on_pb_set_mark_1_clicked()
{
    pcb_mark_param mark_param;

    if(m_pap_thread->m_pcb_mark_list.contains(1)) {
        QMessageBox::StandardButton ret;

        ret = QMessageBox::warning(this, tr("Mark set warning"), "Confirm to Resetting Mark 1?",
                                         QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
        if(ret != QMessageBox::Yes) {
            return;
        }
    }

    if(!get_current_part_coordinate(mark_param.mark_coor)) {
        disp_system_msg("Error: Please select one mark in list");
        return;
    }
    mark_param.arm_pos = m_pap_thread->m_curr_arm_pos;

    m_pap_thread->m_pcb_mark_list[1] = mark_param;
}

void Hell_Arm::on_pb_connect_device_clicked()
{
    m_pap_thread->usb_connect_ctrl();
    if(m_pap_thread->m_usb_dev == NULL) {
        ui->pb_connect_device->setText("Connect...");
    } else {
        ui->pb_connect_device->setText("Disconnect");
    }
}

void Hell_Arm::on_pb_clear_sys_msg_clicked()
{
    ui->pte_system_msg->clear();
}

#define REPEAT_SPEED_K      9
void Hell_Arm::on_pb_arm_move_left_clicked()
{
    iPoint target = m_pap_thread->m_curr_arm_pos;
    if(ui->pb_arm_move_left->isDown()) {
        target.x -= REPEAT_SPEED_K*MANUAL_MOVE_MIN_STEP;
    }
    target.x -= MANUAL_MOVE_MIN_STEP;
    if(!m_pap_thread->move_arm_to(target)) {
        disp_system_msg("Move left fail");
    }
}

void Hell_Arm::on_pb_arm_move_right_clicked()
{
    iPoint target = m_pap_thread->m_curr_arm_pos;
    if(ui->pb_arm_move_right->isDown()) {
        target.x += REPEAT_SPEED_K*MANUAL_MOVE_MIN_STEP;
    }
    target.x += MANUAL_MOVE_MIN_STEP;

    if(!m_pap_thread->move_arm_to(target)) {
        disp_system_msg("Move right fail");
    }
}


void Hell_Arm::on_pb_arm_move_back_clicked()
{
    iPoint target = m_pap_thread->m_curr_arm_pos;
    if(ui->pb_arm_move_back->isDown()) {
        target.y += REPEAT_SPEED_K*MANUAL_MOVE_MIN_STEP;
    }
    target.y += MANUAL_MOVE_MIN_STEP;
    if(!m_pap_thread->move_arm_to(target)) {
        disp_system_msg("Move back fail");
    }
}

void Hell_Arm::on_pb_arm_move_front_clicked()
{
    iPoint target = m_pap_thread->m_curr_arm_pos;
    if(ui->pb_arm_move_front->isDown()) {
        target.y -= REPEAT_SPEED_K*MANUAL_MOVE_MIN_STEP;
    }
    target.y -= MANUAL_MOVE_MIN_STEP;

    if(!m_pap_thread->move_arm_to(target)) {
        disp_system_msg("Move front fail");
    }
}

void Hell_Arm::on_vsb_pick_up_down_ctrl_valueChanged(int value)
{
    m_pap_thread->set_picker_updown(PI * (180 - value)/180);
    ui->vsb_pick_up_down_ctrl->setToolTip(QString("%1, %2").arg(value).arg(value*PI/180));
}

void Hell_Arm::on_hsb_picker_angle_ctrl_valueChanged(int value)
{
    m_pap_thread->set_picker_angle(PI * value/180);
    ui->hsb_picker_angle_ctrl->setToolTip(QString("%1, %2").arg(value).arg(value*PI/180));
}

void Hell_Arm::on_pb_debug_clicked()
{
    iPoint target;

    target.x = 0;
    target.y = 0;
    if(!m_pap_thread->move_arm_to(target, 1000)) {
        return;
    }

    target.x = 3000;
    target.y = 3000;
    if(!m_pap_thread->move_arm_to(target, 1000)) {
        return;
    }
    msdelay(500);
    target.x = 0;
    target.y = 0;
    if(!m_pap_thread->move_arm_to(target, 1000)) {
        return;
    }

}

void Hell_Arm::on_pb_origin_init_clicked()
{
    QMessageBox::StandardButton ret;

    ret = QMessageBox::warning(this, tr("Origin set warning"), "Confirm to set origin locate",
                                     QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if(ret == QMessageBox::Yes) {
        m_pap_thread->m_curr_arm_pos.x = 0;
        m_pap_thread->m_curr_arm_pos.y = 0;

        m_origin_zone_size = m_vision_thread->get_feeder_zone_size();
        m_origin_zone_area = m_vision_thread->get_part_area();
        disp_system_msg("arm origin set");
    }
}

void Hell_Arm::on_sb_stepmotor_move_speed_valueChanged(int value)
{
    if(!m_pap_thread->set_move_speed(value)) {
        if(m_pap_thread->m_usb_dev != NULL) {
            disp_system_msg("Set move speed fail");
        }
    }
}

void Hell_Arm::on_cb_only_pickup_clicked(bool checked)
{
    m_pap_thread->set_only_pickup_flag(checked);
}

void Hell_Arm::slot_xy_axis_pos(int x, int y)
{
    ui->lcd_x_axis_offset->display(x);
    ui->lcd_y_axis_offset->display(-y);
}

void Hell_Arm::on_hsb_set_feeder_zone_width_valueChanged(int value)
{
    ui->hsb_set_feeder_zone_width->setToolTip(QString::number(value));
    m_vision_thread->set_feeder_zone_size(QSize(value, ui->hsb_set_feeder_zone_height->value()));
}

void Hell_Arm::on_hsb_set_feeder_zone_height_valueChanged(int value)
{
    ui->hsb_set_feeder_zone_height->setToolTip(QString::number(value));
    m_vision_thread->set_feeder_zone_size(QSize(ui->hsb_set_feeder_zone_width->value(), value));
}

void Hell_Arm::on_sb_set_part_area_valueChanged(int value)
{
    int step;

    m_vision_thread->set_part_area(value);
    if(value >= 10000) {
        step = 500;
    } else if(value >= 1000) {
        step = 100;
    } else{
        step = 50;
    }
    ui->sb_set_part_area->setSingleStep(step);
}

void Hell_Arm::on_dsb_cam_width_length_valueChanged(double len)
{
    m_pap_thread->set_cam_width_length_mm(len);
}

void Hell_Arm::on_pb_lock_focus_for_move_clicked(bool checked)
{
    if(checked) {
        this->setFocus();
    }
}

void Hell_Arm::on_cb_pump_control_clicked(bool checked)
{
    if(!ui->pb_auto_pick_and_place->isChecked()){
        m_pap_thread->turn_pump_on(checked);
    }
}

void Hell_Arm::on_sb_picker_offset_x_valueChanged(int value)
{
    m_pap_thread->set_picker_offset(value, ui->sb_picker_offset_y->value());
}

void Hell_Arm::on_sb_picker_offset_y_valueChanged(int value)
{
    m_pap_thread->set_picker_offset(ui->sb_picker_offset_x->value(), value);
}

void Hell_Arm::on_pb_save_cfg_to_file_clicked()
{
    QString file_path = QFileDialog::getSaveFileName(this, tr("Save Setting"),
                                                     "", tr("Setting (*.cfg);;All (*.*)"));
    if(file_path.isEmpty()) {
        return;
    }

    QFileInfo file_info(file_path);

    if(!file_info.fileName().contains('.')) {
        file_path += ".cfg";
    }

    QString current_path = QDir::currentPath();
    if(file_path.contains(current_path)) {
        file_path.remove(current_path + "/");
    }

    if(m_config_file != NULL) {
        delete m_config_file;
    }
    m_config_file = new QSettings(file_path, QSettings::IniFormat);
    store_config();

    m_choose_cfg_file->add_config_file(file_path);
    m_choose_cfg_file->show();
}

void Hell_Arm::on_pb_move_arm_to_take_out_pos_clicked()
{
    if(ui->pb_auto_pick_and_place->isChecked()) {
        return;
    }

    if(m_pap_thread->picker_move_to_top()) {
        if(!m_pap_thread->move_arm_to(m_arm_take_out_pos, 1000)) {
            disp_system_msg("Move arm to take out position FAIL");
        }
    } else {
        disp_system_msg("Fail to move picker to top");
    }
}

void Hell_Arm::on_pb_set_arm_take_out_pos_clicked()
{
    QMessageBox::StandardButton ret;

    ret = QMessageBox::warning(this, tr("Take out position set warning"), "Confirm to set Take out position?",
                                     QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if(ret != QMessageBox::Yes) {
        return;
    }

    m_arm_take_out_pos = m_pap_thread->m_curr_arm_pos;
}
